package com.itee.javafx.controls;


import com.itee.core.serial.*;
import com.itee.javafx.ExecutorsUtils;
import com.itee.monitor.codec.NodeCodecFactory;
import gnu.io.PortInUseException;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.serial.SerialAddress;
import org.apache.mina.transport.serial.SerialPortUnavailableException;
import org.controlsfx.dialog.Dialogs;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

/**
 * Created by gonglei on 14-4-22.
 */
public class SerialSettingPane extends GridPane {

    private static final Integer[] BAUDS = {110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 230400, 380400, 460800, 921600};

    @FXML
    private ComboBox port;

    @FXML
    private ComboBox bauds;

    @FXML
    private ComboBox dataBits;

    @FXML
    private ComboBox parity;

    @FXML
    private ComboBox stopBits;

    @FXML
    private ComboBox flowControl;

    @FXML
    private Button btnOpen;

    @FXML
    private Button btnClose;

    @FXML
    private Button btnRefresh;

    @FXML
    private Label serialTitleLabel;

    private SerialUtils serialUtils;
    private SerialHandler serialHandler;

    protected String serialName;

    private String serialTitle;

    public SerialSettingPane() {
        serialHandler = SerialHandler.getInstance();
        serialUtils = new SerialUtils(serialHandler, new NodeCodecFactory());

        FXMLLoader fXMLLoader = new FXMLLoader(getClass().getResource("SerialSettingPane.fxml"));
        fXMLLoader.setRoot(this);
        fXMLLoader.setController(this);
        try {
            fXMLLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
        bauds.getItems().addAll(BAUDS);
        dataBits.getItems().addAll(SerialAddress.DataBits.values());
        parity.getItems().addAll(SerialAddress.Parity.values());
        stopBits.getItems().addAll(SerialAddress.StopBits.values());
        flowControl.getItems().addAll(SerialAddress.FlowControl.values());

        bauds.getSelectionModel().select(Integer.valueOf(9600));
        dataBits.getSelectionModel().select(SerialAddress.DataBits.DATABITS_8);
        parity.getSelectionModel().select(SerialAddress.Parity.EVEN);
        stopBits.getSelectionModel().select(SerialAddress.StopBits.BITS_1);
        flowControl.getSelectionModel().select(SerialAddress.FlowControl.NONE);

        Task<String[]> task = new Task<String[]>() {

            @Override
            protected String[] call() throws Exception {
                return SerialUtils.listPortChoics();
            }

            @Override
            protected void succeeded() {
                try {
                    port.getItems().addAll(get());
                    port.getSelectionModel().selectFirst();
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        ExecutorsUtils.getInstance().execute(task);

        btnClose.setDisable(true);
    }

    /**
     * 关闭串口
     *
     * @param actionEvent
     */
    @FXML
    public void handleCloseSerialButtonAction(ActionEvent actionEvent) {
        SerialSession session = serialHandler.getSession(serialName);
        if (session != null) {
            session.close();
        }

        btnClose.setDisable(true);
        btnOpen.setDisable(false);
        disableSettings(false);
    }

    /**
     * 打开串口
     *
     * @param event
     */
    @FXML
    public void handleOpenSerialButtonAction(ActionEvent event) {
        String port = (String) this.port.getSelectionModel().getSelectedItem();
        if (port == null) {
            Dialogs.create().masthead(null).title("警告").message("请选择端口").showWarning();
            return;
        }

        // 设置串口参数并连接
        SerialParam serialParam = new SerialParam();
        serialParam.setName(port);
        serialParam.setBauds((Integer) bauds.getSelectionModel().getSelectedItem());
        serialParam.setDataBits((SerialAddress.DataBits) dataBits.getSelectionModel().getSelectedItem());
        serialParam.setParity((SerialAddress.Parity) parity.getSelectionModel().getSelectedItem());
        serialParam.setStopBits((SerialAddress.StopBits) stopBits.getSelectionModel().getSelectedItem());
        serialParam.setFlowControl((SerialAddress.FlowControl) flowControl.getSelectionModel().getSelectedItem());

        try {
            IoSession session = serialUtils.connect(serialParam);
            SerialSession serialSession = SerialPortFactory.getSession(serialName, session);
            serialHandler.addSession(serialSession);

            btnOpen.setDisable(true);
            btnClose.setDisable(false);
            disableSettings(true);
        } catch (Exception e) {
            Throwable cause = e;
            for (Throwable t = e.getCause(); t != null; t = cause.getCause()) {
                cause = t;
            }

            if (cause instanceof SerialPortUnavailableException) {
                Dialogs.create().masthead(null).title("提示").message("端口不存在，请检查设置").showInformation();
            } else if (cause instanceof PortInUseException) {
                Dialogs.create().masthead(null).title("提示").message("端口已被占用").showInformation();
            } else {
                e.printStackTrace();
            }
        }
    }

    private void disableSettings(boolean disable) {
        btnRefresh.setDisable(disable);
        port.setDisable(disable);
        bauds.setDisable(disable);
        dataBits.setDisable(disable);
        parity.setDisable(disable);
        stopBits.setDisable(disable);
        flowControl.setDisable(disable);
    }

    @FXML
    public void handleRefreshButtonAction(ActionEvent event) {
        port.getItems().setAll(SerialUtils.listPortChoics());
    }

    public String getSerialName() {
        return serialName;
    }

    public void setSerialName(String serialName) {
        this.serialName = serialName;
    }

    public String getSerialTitle() {
        return serialTitle;
    }

    public void setSerialTitle(String serialTitle) {
        this.serialTitle = serialTitle;
        this.serialTitleLabel.setText(serialTitle);
    }
}
